package com.inepex.inechart.shared;

import com.inepex.inechart.shared.axes.Axis;
import com.inepex.inechart.shared.axes.AxisDirection;

public class CartesianAssist {
	
	protected Axis xAxis;
	protected Axis yAxis;
	protected int chartWidth;
	protected int chartHeight;
	protected double[] padding;
	
	//parameters
	protected Axis horizontalAxis;
	protected Axis verticalAxis;
	protected int usableCanvasHeight;
	protected int usableCanvasWidth;
	protected double viewportWidth;
	protected double viewportHeight;
	protected double horizontalMin;
	protected double verticalMin;
	protected double verticalCanvasModelRatio;
	protected double horizontalCanvasModelRatio;
	
	public CartesianAssist() {
		padding = new double[4];
	}
	
	public CartesianAssist(double[] padding){
		if(padding != null && padding.length == 4){
			this.padding = padding;
		}
		else{
			this.padding = new double[4];
		}
	}
	
	protected void setCharSize(int canvasWidth, int canvasHeight){
		this.chartHeight = canvasHeight;
		this.chartWidth = canvasWidth;
		calculateParameters();
	}
	
	protected void calculateParameters(){
		horizontalAxis = xAxis.isHorizontal() ? xAxis : yAxis;
		verticalAxis = !yAxis.isHorizontal() ? yAxis : xAxis; 
		usableCanvasWidth = (int) (chartWidth - padding[1] - padding[3]);
		usableCanvasHeight = (int) (chartHeight - padding[0] - padding[2]);
		viewportHeight = verticalAxis.getMax() - verticalAxis.getMin();
		viewportWidth = horizontalAxis.getMax() - horizontalAxis.getMin();
		horizontalMin = horizontalAxis.getMin();
		verticalMin = verticalAxis.getMin();
		verticalCanvasModelRatio = usableCanvasHeight / viewportHeight;
		horizontalCanvasModelRatio = usableCanvasWidth / viewportWidth;
	}
	
	/**
	 * Model to canvas calculation
	 * @param value on the horizontal axis
	 */
	public double modelToCanvasHorizontal(double value) {
		double pos;
		if (horizontalAxis.getAxisDirection() == AxisDirection.Horizontal_Ascending_To_Right) { // =>
			pos =  (value - horizontalMin) * horizontalCanvasModelRatio	+ padding[3];
		} 
		else { // <=
			pos = (usableCanvasWidth - ((value - horizontalMin) * horizontalCanvasModelRatio)) + padding[3];
		}
		return pos;
	}
	
	/**
	 * Model to canvas calculation
	 * @param value on the vertical axis
	 */
	public double modelToCanvasVertical(double value) {
		double pos;
		if (verticalAxis.getAxisDirection() == AxisDirection.Vertical_Ascending_To_Bottom) { // V
			pos = ((value - verticalMin) * verticalCanvasModelRatio) + padding[0];
		}
		else {
			pos = (usableCanvasHeight - (value - verticalMin) * verticalCanvasModelRatio) + padding[0]; //T
		}
		return pos;
	}
	
	/**
	 * Returns a value for a x position over canvas 
	 * @param canvasX
	 * @return x or y (depends on horizontal axis)
	 */
	public double canvasToModelHorizontal(double canvasX){
		double pos;
		if (horizontalAxis.getAxisDirection() == AxisDirection.Horizontal_Ascending_To_Right) {
			pos =  (canvasX - padding[3]) / horizontalCanvasModelRatio + horizontalMin;
		} 
		else {
			pos =  (usableCanvasWidth + padding[3] - canvasX) / horizontalCanvasModelRatio + horizontalMin;
		}
		return pos;
	}
	
	/**
	 * Returns a value for an y position over canvas 
	 * @param canvasY
	 * @return y or x (depends on vertical axis)
	 */
	public double canvasToModelVertical(double canvasY){
		double pos;
		if (verticalAxis.getAxisDirection() == AxisDirection.Vertical_Ascending_To_Bottom) {
			pos = (canvasY - padding[0]) / verticalCanvasModelRatio + verticalMin;
		}
		else {
			pos = (usableCanvasHeight + padding[0] - canvasY) / verticalCanvasModelRatio + verticalMin;
		}
		return pos;
	}
	
	/**
	 * Translates a point over the canvas to a point in a model
	 * @param canvasX
	 * @param canvasY
	 * @return model's [x,y]
	 */
	public double[] canvasToModel(double canvasX, double canvasY){
		double[] ret = new double[2];
		if (xAxis.isHorizontal()){
			ret[0] = canvasToModelHorizontal(canvasX);
			ret[1] = canvasToModelVertical(canvasY);
		}
		else{
			ret[0] = canvasToModelVertical(canvasY);
			ret[1] = canvasToModelHorizontal(canvasX);
		}			
		return ret;
	}
	
	/**
	 * Translates a point(x,y) in model to a point on canvas
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
 	public double[] modelToCanvas(double x, double y) {
		double[] ret = new double[2];
		if (xAxis.isHorizontal()){
			ret[0] = modelToCanvasHorizontal(x);
			ret[1] = modelToCanvasVertical(y);
		}
		else{
			ret[0] = modelToCanvasVertical(x);
			ret[1] = modelToCanvasHorizontal(y);
		}
		return ret;
	}

 	public int getChartWidth() {
		return chartWidth;
	}
 
 	public int getChartHeight() {
		return chartHeight;
	}

	public int getWidth() {
		return usableCanvasWidth;
	}

	public int getHeight() {
		return usableCanvasHeight;
	}

	public int getRightEnd(){
		return (int) (chartWidth - padding[1]);
	}

	public int getBottomEnd(){
		return (int) (chartHeight- padding[2]);
	}

	public double[] getViewport(){
		return new double[]{horizontalMin, horizontalMin + viewportWidth, verticalMin, verticalMin + viewportHeight};
	}
	
	public double[] getPadding() {
		return padding;
	}

	public double getHorizontalMin() {
		return horizontalMin;
	}

	public double getVerticalMin() {
		return verticalMin;
	}

	public Axis getxAxis() {
		return xAxis;
	}

	public Axis getyAxis() {
		return yAxis;
	}

	public Axis getHorizontalAxis() {
		return horizontalAxis;
	}

	public Axis getVerticalAxis() {
		return verticalAxis;
	}

	public double getViewportWidth() {
		return viewportWidth;
	}

	public double getViewportHeight() {
		return viewportHeight;
	}

	public double getVerticalCanvasModelRatio() {
		return verticalCanvasModelRatio;
	}

	public double getHorizontalCanvasModelRatio() {
		return horizontalCanvasModelRatio;
	}		

	public void setLeftPadding(double leftPadding) {
		this.padding[3] = leftPadding;
	}
	
	public double getLeftPadding() {
		return padding[3];
	}

	public double getTopPadding() {
		return padding[0];
	}

	public void setTopPadding(double topPadding) {
		this.padding[0] = topPadding;
	}

	public double getBottomPadding() {
		return padding[2];
	}

	public void setBottomPadding(double bottomPadding) {
		this.padding[2] = bottomPadding;
	}

	public double getRightPadding() {
		return padding[1];
	}

	public void setRightPadding(double rightPadding) {
		this.padding[1] = rightPadding;
	}
	
	
}
